/*!
  \file GS_util.c
 
  \brief OGSF library - loading and manipulating surfaces
 
  GRASS OpenGL gsurf OGSF Library 
 
  (C) 1999-2008 by the GRASS Development Team
 
  This program is free software under the 
  GNU General Public License (>=v2). 
  Read the file COPYING that comes with GRASS
  for details.
  
  \author Bill Brown USACERL, GMSL/University of Illinois
  \author Doxygenized by Martin Landa <landa.martin gmail.com> (May 2008)
*/

#include <stdlib.h>
#include <math.h>
#include <string.h>

#include <grass/gis.h>
#include <grass/gstypes.h>

/*!
  \brief Calculate distance between 2 coordinates

  Units is one of:
   - "meters",
   - "miles",
   - "kilometers",
   - "feet",
   - "yards",
   - "nmiles" (nautical miles),
   - "rods",
   - "inches",
   - "centimeters",
   - "millimeters",
   - "micron",
   - "nanometers",
   - "cubits",
   - "hands",
   - "furlongs",
   - "chains"

  Default is meters.

  \param from starting point
  \param to ending point
  \param units map units

  \return distance between two geographic coordinates in current projection
*/
double GS_geodistance(double *from, double *to, const char *units)
{
    double meters;

    meters = Gs_distance(from, to);

    if (!units) {
	return (meters);
    }

    if (strcmp(units, "meters") == 0) {
	return (meters);
    }

    if (strcmp(units, "miles") == 0) {
	return (meters * .0006213712);
    }

    if (strcmp(units, "kilometers") == 0) {
	return (meters * .001);
    }

    if (strcmp(units, "feet") == 0) {
	return (meters * 3.280840);
    }

    if (strcmp(units, "yards") == 0) {
	return (meters * 1.093613);
    }

    if (strcmp(units, "rods") == 0) {
	return (meters * .1988388);
    }

    if (strcmp(units, "inches") == 0) {
	return (meters * 39.37008);
    }

    if (strcmp(units, "centimeters") == 0) {
	return (meters * 100.0);
    }

    if (strcmp(units, "millimeters") == 0) {
	return (meters * 1000.0);
    }

    if (strcmp(units, "micron") == 0) {
	return (meters * 1000000.0);
    }

    if (strcmp(units, "nanometers") == 0) {
	return (meters * 1000000000.0);
    }

    if (strcmp(units, "cubits") == 0) {
	return (meters * 2.187227);
    }

    if (strcmp(units, "hands") == 0) {
	return (meters * 9.842520);
    }

    if (strcmp(units, "furlongs") == 0) {
	return (meters * .004970970);
    }

    if (strcmp(units, "nmiles") == 0) {
	/* nautical miles */
	return (meters * .0005399568);
    }

    if (strcmp(units, "chains") == 0) {
	return (meters * .0497097);
    }

    return (meters);
}

/*!
  \brief Calculate distance
  
  \param from 'from' point (X,Y,Z)
  \param to 'to' point (X,Y,Z)

  \return distance
*/
float GS_distance(float *from, float *to)
{
    float x, y, z;

    x = from[X] - to[X];
    y = from[Y] - to[Y];
    z = from[Z] - to[Z];

    return (float) sqrt(x * x + y * y + z * z);
}

/*!
  \brief Calculate distance in plane

  \param from 'from' point (X,Y)
  \param to 'to' point (X,Y)

  \return distance
*/  
float GS_P2distance(float *from, float *to)
{
    float x, y;

    x = from[X] - to[X];
    y = from[Y] - to[Y];

    return (float) sqrt(x * x + y * y);
}

/*!
  \brief Copy vector values

  v1 = v2

  \param[out] v1 first vector
  \param v2 second vector
*/
void GS_v3eq(float *v1, float *v2)
{
    v1[X] = v2[X];
    v1[Y] = v2[Y];
    v1[Z] = v2[Z];

    return;
}

/*!
  \brief Sum vectors
  
  v1 += v2

  \param[in,out] v1 first vector
  \param v2 second vector
*/
void GS_v3add(float *v1, float *v2)
{
    v1[X] += v2[X];
    v1[Y] += v2[Y];
    v1[Z] += v2[Z];

    return;
}

/*!
  \brief Subtract vectors

  v1 -= v2

  \param[in,out] v1 first vector
  \param v2 second vector
*/  
void GS_v3sub(float *v1, float *v2)
{
    v1[X] -= v2[X];
    v1[Y] -= v2[Y];
    v1[Z] -= v2[Z];

    return;
}

/*!
  \brief Multiple vectors

  v1 *= k

  \param[in,out] v1 vector
  \param k multiplicator
*/
void GS_v3mult(float *v1, float k)
{
    v1[X] *= k;
    v1[Y] *= k;
    v1[Z] *= k;

    return;
}

/*!
  \brief Change v1 so that it is a unit vector (2D)

  \param[in,out] v1 vector

  \return 0 if magnitude of v1 is zero
  \return 1 if magnitude of v1 > 0
*/
int GS_v3norm(float *v1)
{
    float n;

    n = sqrt(v1[X] * v1[X] + v1[Y] * v1[Y] + v1[Z] * v1[Z]);

    if (n == 0.0) {
	return (0);
    }

    v1[X] /= n;
    v1[Y] /= n;
    v1[Z] /= n;

    return (1);
}

/*!
  \brief Change v1 so that it is a unit vector (3D)

  \param[in,out] v1 vector

  \return 0 if magnitude of v1 is zero
  \return 1 if magnitude of v1 > 0
*/
int GS_v2norm(float *v1)
{
    float n;

    n = sqrt(v1[X] * v1[X] + v1[Y] * v1[Y]);

    if (n == 0.0) {
	return (0);
    }

    v1[X] /= n;
    v1[Y] /= n;

    return (1);
}

/*!
  \brief Changes v1 so that it is a unit vector

  \param dv1 vector

  \return 0 if magnitude of dv1 is zero
  \return 1 if magnitude of dv1 > 0
*/
int GS_dv3norm(double *dv1)
{
    double n;

    n = sqrt(dv1[X] * dv1[X] + dv1[Y] * dv1[Y] + dv1[Z] * dv1[Z]);

    if (n == 0.0) {
	return (0);
    }

    dv1[X] /= n;
    dv1[Y] /= n;
    dv1[Z] /= n;

    return (1);
}


/*!
  \brief Change v2 so that v1v2 is a unit vector

  \param v1 first vector
  \param v2[in,out] second vector

  \return 0 if magnitude of dx is zero
  \return 1 if magnitude of dx > 0
*/
int GS_v3normalize(float *v1, float *v2)
{
    float n, dx, dy, dz;

    dx = v2[X] - v1[X];
    dy = v2[Y] - v1[Y];
    dz = v2[Z] - v1[Z];
    n = sqrt(dx * dx + dy * dy + dz * dz);

    if (n == 0.0) {
	return (0);
    }

    v2[X] = v1[X] + dx / n;
    v2[Y] = v1[Y] + dy / n;
    v2[Z] = v1[Z] + dz / n;

    return (1);
}


/*!
  \brief Get a normalized direction from v1 to v2, store in v3

  \param v1 first vector
  \param v2 second vector
  \param[out] v3 output vector

  \return 0 if magnitude of dx is zero
  \return 1 if magnitude of dx > 0
*/
int GS_v3dir(float *v1, float *v2, float *v3)
{
    float n, dx, dy, dz;

    dx = v2[X] - v1[X];
    dy = v2[Y] - v1[Y];
    dz = v2[Z] - v1[Z];
    n = sqrt(dx * dx + dy * dy + dz * dz);

    if (n == 0.0) {
	v3[X] = v3[Y] = v3[Z] = 0.0;
	return (0);
    }

    v3[X] = dx / n;
    v3[Y] = dy / n;
    v3[Z] = dz / n;

    return (1);
}


/*!
  \brief Get a normalized direction from v1 to v2, store in v3 (2D)

  \param v1 first vector
  \param v2 second vector
  \param[out] v3 output vector

  \return 0 if magnitude of dx is zero
  \return 1 if magnitude of dx > 0
*/
void GS_v2dir(float *v1, float *v2, float *v3)
{
    float n, dx, dy;

    dx = v2[X] - v1[X];
    dy = v2[Y] - v1[Y];
    n = sqrt(dx * dx + dy * dy);

    v3[X] = dx / n;
    v3[Y] = dy / n;

    return;
}

/*!
  \brief Get the cross product v3 = v1 cross v2

  \param v1 first vector
  \param v2 second vector
  \param[out] v3 output vector
*/
void GS_v3cross(float *v1, float *v2, float *v3)
{
    v3[X] = (v1[Y] * v2[Z]) - (v1[Z] * v2[Y]);
    v3[Y] = (v1[Z] * v2[X]) - (v1[X] * v2[Z]);
    v3[Z] = (v1[X] * v2[Y]) - (v1[Y] * v2[X]);

    return;
}

/*!
  \brief Magnitude of vector

  \param v1 vector
  \param[out] mag magnitude value
*/
void GS_v3mag(float *v1, float *mag)
{
    *mag = sqrt(v1[X] * v1[X] + v1[Y] * v1[Y] + v1[Z] * v1[Z]);

    return;
}

/*!
  \brief ADD

  Initialize by calling with a number nhist to represent number of
  previous entrys to check, then call with zero as nhist

  \param p1 first point
  \param p2 second point
  \param nhist ?

  \return -1 on error
  \return -2
  \return 1
  \return 9
*/
int GS_coordpair_repeats(float *p1, float *p2, int nhist)
{
    static float *entrys = NULL;
    static int next = 0;
    static int len = 0;
    int i;

    if (nhist) {
	if (entrys) {
	    G_free(entrys);
	}

	entrys = (float *) G_malloc(4 * nhist * sizeof(float));

	if (!entrys)
	  return (-1);

	len = nhist;
	next = 0;
    }

    if (!len) {
	return (-2);
    }

    for (i = 0; i < next; i += 4) {
	if (entrys[i] == p1[0] && entrys[i + 1] == p1[1]
	    && entrys[i + 2] == p2[0] && entrys[i + 3] == p2[1]) {
	    return (1);
	}
    }

    if (len == next / 4) {
	next = 0;
    }

    entrys[next] = p1[0];
    entrys[next + 1] = p1[1];
    entrys[next + 2] = p2[0];
    entrys[next + 3] = p2[1];
    next += 4;

    return (0);
}
